<template>
  <div>
    <div class="signature" :style="{ width: width, height: height }">
      <canvas style="touch-action: none; user-select: none" ref="signatureArea"></canvas>
    </div>
    <div class="btn">
      <van-button @click="clear">清空</van-button>
      <van-button type="primary" @click="onSubmit">完成</van-button>
    </div>
  </div>
</template>

<script>
import SignaturePad from 'signature_pad';
import { Toast } from 'vant';

export default {
  emits: ['result'],
  props: {
    value: {
      type: String
    },
    originBound: {
      type: Object,
      required: false
    },
    width: {
      type: [String, Number],
      default: '100%',
      required: false
    },
    height: {
      type: [String, Number],
      default: '200px',
      required: false
    },
    backgroundColor: {
      type: String,
      required: false
    },
    penColor: {
      type: String,
      default: '#000000',
      required: false
    },
    autoTrim: {
      type: Boolean,
      default: false,
      required: false
    },
    debug: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      canvas: null,
      signaturePad: null,
      bound: null
    };
  },
  watch: {
    value (newVal) {
      if (!newVal) {
        this.signaturePad.clear();
      } else {
        this.drawSignature();
      }
    }
  },
  methods: {
    clear () {
      this.signaturePad.clear();
    },
    onSubmit () {
      if (this.signaturePad.isEmpty()) {
        Toast({
          message: '还没有签字内容',
          position: 'top'
        });
      } else {
        const dataURL = this.toDataURL();
        if (dataURL === null) {
          Toast({
            message: '还没有签字内容',
            position: 'top'
          });
          return;
        }
        const result = {
          data: dataURL,
          bound: this.bound || {
            top: 0,
            left: 0,
            right: this.canvas.width,
            bottom: this.canvas.height
          }
        };
        this.$emit('result', result);
      }
    },
    // Adjust canvas coordinate space taking into account pixel ratio,
    // to make it look crisp on mobile devices.
    // This also causes canvas to be cleared.
    resizeCanvas () {
      // When zoomed out to less than 100%, for some very strange reason,
      // some browsers report devicePixelRatio as less than 1
      // and only part of the canvas is cleared then.
      const ratio = Math.max(window.devicePixelRatio || 1, 1);

      if (this.debug) {
        console.log('signaturePad.resizeCanvas() ratio', ratio);
        console.log('signaturePad.resizeCanvas() offsetWidth', this.canvas.offsetWidth);
        console.log('signaturePad.resizeCanvas() offsetHeight', this.canvas.offsetHeight);
      }

      // This part causes the canvas to be cleared
      this.canvas.width = this.canvas.offsetWidth * ratio;
      this.canvas.height = this.canvas.offsetHeight * ratio;
      this.canvas.getContext('2d').scale(ratio, ratio);
      // this.canvas.style.width = this.canvas.offsetWidth + 'px'; // 设置CSS宽度，以像素为单位，与实际宽度一致以保持清晰度在高清屏上。
      // this.canvas.style.height = this.canvas.offsetHeight + 'px'; // 同上。

      // This library does not listen for canvas changes, so after the canvas is automatically
      // cleared by the browser, SignaturePad#isEmpty might still return false, even though the
      // canvas looks empty, because the internal data of this library wasn't cleared. To make sure
      // that the state of this library is consistent with visual state of the canvas, you
      // have to clear it manually.
      // ignaturePad.clear();

      // If you want to keep the drawing on resize instead of clearing it you can reset the data.
      // this.signaturePad.fromData(this.signaturePad.toData());
    },
    getTrimmedCanvas () {
      const ctx = this.canvas.getContext('2d');
      const imageData = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
      const pixels = imageData.data;
      const l = pixels.length;
      const bound = { top: null, left: null, right: null, bottom: null };
      this.bound = bound;

      // 找到签名的实际边界
      for (let i = 0; i < l; i += 4) {
        if (pixels[i + 3] !== 0) {
          // 检查 alpha 通道是否不透明
          const x = (i / 4) % this.canvas.width;
          const y = ~~(i / 4 / this.canvas.width);

          if (bound.top === null) {
            bound.top = y;
          }

          if (bound.left === null) {
            bound.left = x;
          } else if (x < bound.left) {
            bound.left = x;
          }

          if (bound.right === null) {
            bound.right = x;
          } else if (x > bound.right) {
            bound.right = x;
          }

          if (bound.bottom === null) {
            bound.bottom = y;
          } else if (y > bound.bottom) {
            bound.bottom = y;
          }
        }
      }

      // 如果没有找到任何签名内容，返回 null
      if (bound.top === null) {
        return null;
      }

      // 创建一个新的 canvas 对象
      const trimmedCanvas = document.createElement('canvas');
      const trimmedCtx = trimmedCanvas.getContext('2d');

      // 设置新的 canvas 的宽度和高度
      trimmedCanvas.width = bound.right - bound.left + 1;
      trimmedCanvas.height = bound.bottom - bound.top + 1;

      // 复制签名内容到新的 canvas 上
      trimmedCtx.putImageData(
        ctx.getImageData(bound.left, bound.top, trimmedCanvas.width, trimmedCanvas.height),
        0,
        0
      );

      return trimmedCanvas;
    },
    toDataURL () {
      const trimmedCanvas = this.autoTrim ? this.getTrimmedCanvas() : this.canvas;
      if (trimmedCanvas === null) {
        return null;
      }
      // type 是可选参数，表示图像类型，默认为'image/png'。常见的MIME类型还包括'image/jpeg'和'image/webp'。
      // encoderOptions是可选参数，表示图像质量，取值范围为0到1，只对'image/jpeg'和'image/webp'有效，默认为0.92。
      // const dataURL = trimmedCanvas.toDataURL('image/png', { type: 'image/png', encoderOptions: 1.0 });
      const dataURL = trimmedCanvas.toDataURL();
      // const dataURL = this.signaturePad.toDataURL();
      // const dataURL = signaturePad.toDataURL("image/jpeg");
      // const dataURL = signaturePad.toDataURL('image/svg+xml');
      // const dataURL = signaturePad.toDataURL('image/svg+xml', { includeBackgroundColor: true });
      if (this.debug) {
        console.log('signaturePad.toDataURL()', dataURL);
      }
      return dataURL;
    },
    drawSignature () {
      // 只合适Base64图片
      if (this.value) {
        const ratio = Math.max(window.devicePixelRatio || 1, 1);
        const options = {};
        if (this.originBound) {
          options.xOffset = this.originBound.left / ratio;
          options.yOffset = this.originBound.top / ratio;
          if (this.originBound.right > this.originBound.left) {
            options.width = (this.originBound.right - this.originBound.left) / ratio;
          }
          if (this.originBound.bottom > this.originBound.top) {
            options.height = (this.originBound.bottom - this.originBound.top) / ratio;
          }
        }
        this.signaturePad.fromDataURL(this.value, options);
      }
    }
  },
  mounted () {
    const options = {
      // It's Necessary to use an opaque color when saving image as JPEG;
      // this option can be omitted if only saving as PNG or SVG
      // backgroundColor: 'rgb(255, 255, 255)'
      backgroundColor: this.backgroundColor,
      penColor: this.penColor
    };
    this.$nextTick(() => {
      this.canvas = this.$refs.signatureArea;
      this.signaturePad = new SignaturePad(this.canvas, options);
      // console.log(this.value);
      this.drawSignature();
      this.resizeCanvas();
    });
  }
};
</script>

<style scoped>
.signature {
  border: 1px solid #999;
  padding: 3px;
}

canvas {
  border: 1px dashed #aaa;
  width: 100%;
  height: 100%;
}

.btn {
  margin-top: 10px;
  display: flex;
  flex-direction: row;
  justify-content: space-evenly;
}
</style>
